# Spring Boot
# Spring Boot 出世
随着动态语言的流行 (Ruby、Groovy、Scala、Node.js),Java 的开发显得格外的笨重:J2EE笨重的开发、繁多的配置、依赖管理的耗时耗力、低下的开发效率、复杂的部署流程以及第三方技术集成难度大。
在上述环境下,Spring Boot 应运而生。它使用 “约定大于配置” (项目中存在大量的配置,此外还内置了一个习惯性的配置,让你无需手动进行配置)的理念让你的项目快速的运行起来。使用 Spring Boot 很容易创建一个独立运行(运行 Jar,内嵌 Servlet 容器)准生产级别的基于 Spring 框架的项目,使用 Spring Boot 你可以不用或者只需很少的 Spring 配置。Spring Boot 可以称之为 新一代 Jakarta EE 开发标准
优点:
- 快速构建项目:SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式
- 对主流开发框架的无配置集成
- 项目可独立运行,无需外部依赖 Servlet 容器
- 提供运行时的应用监控
- 极大地提高了开发、部署效率
- 与云计算的天然集成
核心功能
起步依赖(starter-*)
起步依赖本质是一个Maven项目对象模型(POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
自动配置
Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
# Hello World
# POM
使用 Spring Initializr 初始化 Spring Boot 项目: https://start.spring.io/,IDEA中自带。根据需求选择需要的依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--所有的springboot工程都必须继承spring-boot-starter-parent,也可以去掉并自定义-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--该项目的Maven坐标-->
<groupId>top.conanan</groupId>
<artifactId>study-spring-boot</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>study-spring-boot</name>
<description>Demo project for Spring Boot</description>
<!--选择JDK版本后自动配置-->
<properties>
<java.version>11</java.version> <!--Java8 这里显示为1.8-->
</properties>
<!--依赖-->
<dependencies>
<!--web功能的起步依赖,若是个普通Java工程,则会是spring-boot-starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--自动添加的测试配置。Spring Boot 2.2.2目前使用的是 JUnit5-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Spring Boot DevTools提供快速应用程序重启,LiveReload和配置,以增强开发体验。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--Lombok Java注释库,有助于减少样板代码。-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--Spring配置处理器为开发人员生成元数据,以便在使用自定义配置键时提供上下文帮助和“代码完成”(ex.application.properties)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--Spring Boot Actuator支持内置(或自定义)端点,可以监控和管理应用程序,例如应用程序运行状况,指标,会话等。用于 Ops -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<!--自动添加的maven插件,这个插件可将应用打包成可执行的jar包。可以通过java -jar 包名来运行应用-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
# Application
默认生成的 Spring Boot 项目。主程序已经生成好了,我们只需要我们自己的逻辑
//声明该类是一个SpringBoot引导类 @SpringBootApplication public class StudySpringBootApplication { //main是java程序的入口 public static void main(String[] args) { //run方法 表示运行SpringBoot的引导类 run参数就是SpringBoot引导类的字节码对象 SpringApplication.run(StudySpringBootApplication.class, args); } }
# resources
目录结构
static:保存所有的静态资源; js、css、images等templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面);可以使用模板引擎如freemarker、thymeleafapplication.properties:Spring Boot应用的配置文件,可以修改一些默认设置- 可以手动新建目录,如
mapper
# HelloController
必须在引导类StudySpringBootApplication同级包或者子级包中创建,才能被扫描到
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "study spring boot";
}
}
run 后可使用 Terminal 测试:curl http://localhost:8080/hello
上述POM中有actuator用于检查应用状态,curl http://localhost:8080/actuator/health,返回如下,注意只能用于 run 模式运行的应用,debug 模式不起作用
{"status":"UP"}
# Test
// Spring Boot 2.2.2目前使用的是 JUnit5。很多处和 JUnit4 不一样。如不需要添加注解 @RunWith(SpringRunner.class)
// 使用webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT后,每次启动测试都会随机生成一个 port
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class StudySpringBootApplicationTests {
@Autowired
private HelloController helloController;
@LocalServerPort
private int port;
// Test 特有的,只需注入即可
@Autowired
private TestRestTemplate restTemplate;
@Test
void hello() {
System.out.println("hello");
System.out.println(helloController);
}
@Test
public void greetingShouldReturnDefaultMessage() throws Exception {
String s = restTemplate.getForObject("http://localhost:" + port + "/hello", String.class);
System.out.println(s);
}
}
@SpringBootTestannotation tells Spring Boot to go and look for a main configuration class (one with @SpringBootApplication for instance), and use that to start a Spring application context. You can run this test in your IDE or on the command line (mvn test or gradle test) and it should pass.
Maven打包:
mvn clean package -Dmaven.test.skip可以看到带有
.original的为原始包,和带有所有依赖的jar包,可以执行java -jar启动
# 起步依赖 — starter 🔥
按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-starter-parent的pom.xml,xml配置如下(配置了配置文件名称位置等信息):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
按住Ctrl点击pom.xml中的spring-boot-dependencies,跳转到了spring-boot-dependencies的pom.xml,xml配置如下:
<properties>
<activemq.version>5.15.8</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.71</appengine-sdk.version>
<artemis.version>2.6.3</artemis.version>
<aspectj.version>1.9.2</aspectj.version>
......
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
......
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-maven-plugin</artifactId>
<version>${johnzon.version}</version>
</plugin>
......
</plugins>
</pluginManagement>
</build>
从上面的spring-boot-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了。所以起步依赖作用就是进行依赖的传递。
同理spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。
# 自动配置 — @SpringBootApplication 🔥
按住Ctrl点击查看启动类MySpringBootApplication上的注解@SpringBootApplication,源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
... ... ...
}
# @SpringBootConfiguration
等同与@Configuration,既标注该类是Spring的一个配置类
# @EnableAutoConfiguration
SpringBoot 自动配置功能开启。不是用于自己写的 Bean,而是配置三方库中的 Bean
按住Ctrl点击查看该注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
... ... ...
}
# @AutoConfigurationPackage
自动配置包**,**如下注解的简写
@Import(AutoConfigurationPackages.Registrar.class)
# @Import
Spring底层注解@Import,给容器中导入一个组件;导入的组件由AutoConfigurationImportSelector.class指定,这个类有一个方法,通过注解metadata,将主配置类(@SpringBootApplication)所在包及下面所有子包里面的所有组件扫描到Spring容器
按住Ctrl点击查看AutoConfigurationImportSelector源码
public String[] selectImports(AnnotationMetadata annotationMetadata) {
... ... ...
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}
其中,SpringFactoriesLoader.loadFactoryNames 方法的作用就是从META-INF/spring.factories文件中读取指定类对应的全类名的列表,如xxxAutoConfiguration

spring-autoconfigure-metadata.properties 文件中有关自动配置的配置信息如下:
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
......
上面配置文件存在大量的以Configuration为结尾的类名称,这些类就是存有自动配置信息的类,而SpringApplication在获取这些类名后再加载
我们以ServletWebServerFactoryAutoConfiguration为例来分析源码:
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
......
}
其中,@EnableConfigurationProperties(ServerProperties.class)代表加载ServerProperties服务器配置属性类
进入ServerProperties.class源码如下:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
......
}
其中,prefix = "server" 表示SpringBoot配置文件中的前缀,SpringBoot会将配置文件中以server开始的属性映射到该类的字段中。如在application.properties中配置server.port=80即可改变当前服务器的HTTP端口号
自动配置总结
- SpringBoot启动会加载大量的自动配置类
- 我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
- 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;
- 自动配置类对应属性类
- xxxxAutoConfigurartion:自动配置类;给容器中添加组件
- xxxxProperties:封装配置文件中相关属性;
# @ComponentScan
组件扫描,但是仅扫描注解了@SpringBootApplication类所在的同级包和子级包
# 配置信息的查询
SpringBoot的配置文件,主要的目的就是对配置信息进行修改的,但在配置时的key从哪里去查询呢?我们可以查阅官方文档
常用的配置摘抄如下:
# QUARTZ SCHEDULER (QuartzProperties)
spring.quartz.jdbc.initialize-schema=embedded # Database schema initialization mode.
spring.quartz.jdbc.schema=classpath:org/quartz/impl/jdbcjobstore/tables_@@platform@@.sql # Path to the SQL file to use to initialize the database schema.
spring.quartz.job-store-type=memory # Quartz job store type.
spring.quartz.properties.*= # Additional Quartz Scheduler properties.
# ----------------------------------------
# WEB PROPERTIES
# ----------------------------------------
# EMBEDDED SERVER CONFIGURATION (ServerProperties)
server.port=8080 # Server HTTP port.
server.servlet.context-path= # Context path of the application.
server.servlet.path=/ # Path of the main dispatcher servlet.
# HTTP encoding (HttpEncodingProperties)
spring.http.encoding.charset=UTF-8 # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly.
# JACKSON (JacksonProperties)
spring.jackson.date-format= # Date format string or a fully-qualified date format class name. For instance, `yyyy-MM-dd HH:mm:ss`.
# SPRING MVC (WebMvcProperties)
spring.mvc.servlet.load-on-startup=-1 # Load on startup priority of the dispatcher servlet.
spring.mvc.static-path-pattern=/** # Path pattern used for static resources.
spring.mvc.view.prefix= # Spring MVC view prefix.
spring.mvc.view.suffix= # Spring MVC view suffix.
# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
spring.datasource.password= # Login password of the database.
spring.datasource.url= # JDBC URL of the database.
spring.datasource.username= # Login username of the database.
# JEST (Elasticsearch HTTP client) (JestProperties)
spring.elasticsearch.jest.password= # Login password.
spring.elasticsearch.jest.proxy.host= # Proxy host the HTTP client should use.
spring.elasticsearch.jest.proxy.port= # Proxy port the HTTP client should use.
spring.elasticsearch.jest.read-timeout=3s # Read timeout.
spring.elasticsearch.jest.username= # Login username.
我们可以通过在配置文件中启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
========================= AUTO-CONFIGURATION REPORT ========================= Positive matches:(自动配置类启用的) ----------------- DispatcherServletAutoConfiguration matched: - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) - @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition) Negative matches:(没有启动,没有匹配成功的自动配置类) ----------------- ActiveMQAutoConfiguration: Did not match: - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
# 版本号
如2.2.1.RELEASE,Maven 中显示的版本号,文档中不一定这样显示
- 2:主版本。极有可能底层改变,无法兼容旧版本
- 2:次版本。发布新特性,基本需要保证兼容
- 1:增量版本。修复 bug,保证兼容(也不确定)
- RELEASE:里程碑。版本的发布计划、状态
- GA(General Availability):Spring Boot 优先选择
- RC:基本和 GA 等同
- SNAPSHOT:快照
- Alpha
- Beta
# SPI 机制 🔥
Service Provider Interface,应对变化的解决方案。基于 Interface 接口 + 策略模式 + 配置文件
之前讲过的 @Primary 和 @Conditionxx 也可以解决,但是关注的粒度是具体类、对象
而 SPI 关注的是整体解决方案,关注许多类,对象的整体!